# -*- coding: utf-8 -*-
"""
Created on 2011-4-21
微信接口
@author: Jcy
"""
import random
import string
import hashlib
import xml.sax
import xml.sax.handler
import requests
import xmltodict
import time
import logging
from project.settings import wx_settings, DOMAIN_URL

wechat_logger = logging.getLogger("wechat")

WX_MCH_PAY_URL_PREFIX = 'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers'


# 微信企业付款
def mch_pay(trade_no, user_openid, check_name, user_name, amount, desc):

    amount = int(amount) * 100  # 分为单位
    # 转换好的数据
    formatDataString = dealWithRequsetData(trade_no, user_openid, check_name, user_name, amount, desc)

    r = requests.post(WX_MCH_PAY_URL_PREFIX,
                      data=formatDataString.encode('utf-8'),
                      cert=(wx_settings.MCH_SSL_CERT_PATH, wx_settings.MCH_SSL_KEY_PATH))
    out = r.text.encode("utf-8")

    # 对返回结果进行处理
    xh = XMLHandler()
    xml.sax.parseString(out, xh)
    outDic = xh.getDict()

    # 对返回结果进行判断，并自定义处理
    result_code = outDic["result_code"]
    if result_code == "SUCCESS":
        return True, outDic

    else:
        # return_msg = outDic["return_msg"]
        # err_code = outDic["err_code"]
        return False, outDic


# 签名
def md5(str):
    m = hashlib.md5()
    m.update(str)
    out = m.hexdigest()
    return out.upper()


# 处理收到的请求信息，包装成字典
def dealWithRequsetData(trade_no, user_openid, check_name, user_name, amount, desc):
    dic = {}
    # 公众账号appid
    dic["mch_appid"] = wx_settings.APP_ID
    # 商户号
    dic["mchid"] = wx_settings.MCH_ID
    # 设备号
    # dic["device_info"] = "******"
    # 随机字符串
    dic["nonce_str"] = trade_no + ''.join(random.choice(string.ascii_lowercase) for _ in range(5))
    # 商户订单号
    dic["partner_trade_no"] = trade_no

    # 用户openid
    # dic["openid"] = user_openid.encode('utf-8')
    dic["openid"] = user_openid

    dic["check_name"] = check_name

    if check_name in ["OPTION_CHECK", "FORCE_CHECK"]:
        # 收款用户姓名
        dic["re_user_name"] = user_name

    # 金额
    dic["amount"] = str(amount)
    # 企业付款描述信息
    # dic["desc"] = desc.encode('utf-8')
    dic["desc"] = desc

    # Ip地址
    dic["spbill_create_ip"] = "192.168.1.1"

    # ####　　信息格式化为xml

    # 排序
    diction = sorted(iter(dic.items()), key=lambda d: d[0], reverse=False)

    print(diction)
    # 拼接成字符串
    stringtoMD5 = ""
    for i in range(0, len(diction)):
        stringtoMD5 += diction[i][0] + "=" + diction[i][1] + "&"
    stringtoMD5 = stringtoMD5 + "key=" + wx_settings.MCH_SECRET

    # 转换成xml
    xml = ["<xml>"]
    for i in range(0, len(diction)):
        xml.append("<{0}>{1}</{0}>".format(diction[i][0], diction[i][1]))

    # 转换成XML字符串
    xmlstr = "".join(xml)

    # 生成签名
    aftermMD5 = md5(stringtoMD5.encode('utf-8'))

    # 拼接签名
    xmlstr = xmlstr + "<sign>"+aftermMD5+"</sign>"+"</xml>"
    print(xmlstr)

    return xmlstr


# 处理的XMl格式数据
class XMLHandler(xml.sax.handler.ContentHandler):
    def __init__(self):
        xml.sax.handler.ContentHandler.__init__(self)
        self.buffer = ""
        self.mapping = {}

    def startElement(self, name, attributes):
        self.buffer = ""

    def characters(self, data):
        self.buffer += data

    def endElement(self, name):
        self.mapping[name] = self.buffer

    def getDict(self):
        return self.mapping


class WeChatPay(object):

    def __init__(self, pay_type):
        self.notify_url = wx_settings.WECHAT_MOBILE_NOTIFY % (DOMAIN_URL, pay_type)
        self.pay_type = pay_type

    @property
    def _pay_setting(self):
        pay_setting = dict(wx_settings._pay_tuple).get(self.pay_type)
        if pay_setting:
            return pay_setting
        else:
            raise TypeError("Wrong type of pay_type")

    @property
    def _appId(self):
        return self._pay_setting['appid']

    @property
    def _Mchid(self):
        return self._pay_setting['mchid']

    @property
    def _key(self):
        return self._pay_setting['key']

    @property
    def _trade_type(self):
        return self._pay_setting['trade_type']

    def build_unifiedorder(self, params):
        base_params = {
            'appid': self._appId,
            'mch_id': self._Mchid,
            'nonce_str': self.generate_random_string(),
            'trade_type': self._trade_type,
            'body': params['body'],
            'out_trade_no': params['out_trade_no'],
            'total_fee': params['total_fee'],
            'spbill_create_ip': params['spbill_create_ip'],
            'notify_url': self.notify_url,
            'device_info': 'WEB',
            'limit_pay': 'no_credit'
        }
        # 公众号支付
        if self._trade_type == "JSAPI":
            base_params['openid'] = params['openid']

        base_params['sign'] = WeChatPay.build_sign(base_params, self._key)
        return self.dict_to_xml(base_params)

    def build_form_by_params(self, params):
        headers = {'Content-Type': 'application/xml'}
        xml = self.build_unifiedorder(params).encode('utf-8')
        response = requests.post('https://api.mch.weixin.qq.com/pay/unifiedorder', data=xml, headers=headers)
        response.encoding = 'utf-8'
        response_dict = xmltodict.parse(response.text)['xml']
        if response_dict['return_code'] == 'SUCCESS' and response_dict['result_code'] == 'SUCCESS':
            trade_type = str.lower(self._trade_type)
            if trade_type == 'mweb':
                return self.build_form_by_mweb(response_dict, params['out_trade_no'])
            else:
                return eval("self.build_form_by_%s(response_dict)" % trade_type)
        else:
            wechat_logger.error(response_dict)
            print(response_dict)

    def build_form_by_jsapi(self, response_data):
        """
        公众号支付签名用
        """
        base_params = {
            'appId': self._appId,
            'timeStamp': str(int(time.time())),
            'nonceStr': self.generate_random_string(),
            'package': "prepay_id=%s" % response_data['prepay_id'],
            'signType': "MD5"
        }
        base_params['paySign'] = WeChatPay.build_sign(base_params, self._key)
        return base_params

    def build_form_by_app(self, response_data):
        """
        APP支付签名用
        """
        base_params = {
            'appid': self._appId,
            'partnerid': self._Mchid,
            'prepayid': response_data['prepay_id'],
            'package': 'Sign=WXPay',
            'timestamp': str(int(time.time())),
            'noncestr': self.generate_random_string(),
        }
        base_params['sign'] = WeChatPay.build_sign(base_params, self._key)
        return base_params

    def build_form_by_native(self, response_data):
        """
        二维码扫码支付
        """
        return response_data['code_url']

    def build_form_by_mweb(self, response_data, out_trade_no):
        """
        H5扫码支付
        """
        # redict_url = DOMAIN_URL + '/retail/success?order_id=%s' % out_trade_no
        # return response_data['mweb_url'] + '&redirect_url=%s' % redict_url
        return response_data['mweb_url']

    @staticmethod
    def build_sign(params, _key):
        # 对所有传入参数按照字段名的 ASCII 码从小到大排序（字典序）
        keys = list(params.keys())
        keys.sort()
        array = []
        for key in keys:
            # 值为空的参数不参与签名
            if not params[key]:
                continue
            # sign不参与签名
            if key == 'sign':
                continue
            array.append("%s=%s" % (key, params[key]))
        # 使用 URL 键值对的格式拼接成字符串string1
        string1 = "&".join(array)

        # 在 string1 最后拼接上 key=Key(商户支付密钥)得到 stringSignTemp 字符串
        stringSignTemp = string1 + '&key=' + _key

        # 对 stringSignTemp 进行 md5 运算，再将得到的字符串所有字符转换为大写
        m = hashlib.md5(stringSignTemp.encode('utf-8'))
        return m.hexdigest().upper()

    @staticmethod
    def generate_random_string(randomlength=32):
        """
        大写字母+数字随机数
        """
        tempstr = list(string.ascii_uppercase+string.digits)
        random.shuffle(tempstr)
        return ''.join(tempstr[:randomlength])

    @staticmethod
    def dict_to_xml(params):
        xml_elements = ["<xml>", ]
        for (k, v) in list(params.items()):
            if str(v).isdigit():
                xml_elements.append('<%s>%s</%s>' % (k, v, k))
            else:
                xml_elements.append('<%s><![CDATA[%s]]></%s>' % (k, v, k))
        xml_elements.append('</xml>')
        return ''.join(xml_elements)

    @staticmethod
    def verify_notify_string(string, key):
        params = WeChatPay.notify_xml_string_to_dict(string)

        notify_sign = params['sign']
        del params['sign']

        if WeChatPay.build_sign(params, key) == notify_sign:
            return True, params
        return False, params

    @staticmethod
    def notify_xml_string_to_dict(string):
        xml_data = xmltodict.parse(string)['xml']
        params = {}
        for k in xml_data:
            params[k] = xml_data[k]
        return params
